//
//  XMLDictionary.m
//  URLSessionTest
//
//  Created by mac on 2017/7/11.
//  Copyright © 2017年 yyb. All rights reserved.
//


#import "XMLDictionary.h"
typedef NS_ENUM(NSInteger, XMLDictionaryAttributesMode)
{
    XMLDictionaryAttributesModePrefixed = 0, //default//默认值为，这意味着该属性将包含在字典中，并带有_（下划线）前缀，以避免命名空间冲突
    XMLDictionaryAttributesModeDictionary,
    XMLDictionaryAttributesModeUnprefixed,//它将所有属性放在单独的字典中，其中包含没有前缀的属性（可能导致与节点冲突）
    XMLDictionaryAttributesModeDiscard//，这将剥离属性。
};

typedef NS_ENUM(NSInteger, XMLDictionaryNodeNameMode)
{
    XMLDictionaryNodeNameModeRootOnly = 0, //default这意味着节点名称只会包含在根字典中（可以从字典键中推断子节点的名称，但是除了根节点之外，nodeName方法将不起作用）。
    XMLDictionaryNodeNameModeAlways,//这意味着节点名称将包含在带有__name（并且可以使用nodeName）方法访问的字典中
    XMLDictionaryNodeNameModeNever//或者将不会包含`__name'键。
};

static NSString *const XMLDictionaryAttributesKey   = @"__attributes";
static NSString *const XMLDictionaryCommentsKey     = @"__comments";
static NSString *const XMLDictionaryTextKey         = @"__text";
static NSString *const XMLDictionaryNodeNameKey     = @"__name";
static NSString *const XMLDictionaryAttributePrefix = @"_";

@interface NSDictionary (XMLDictionary)

@property (nonatomic, readonly, copy, nullable) NSDictionary<NSString *, NSString *> *attributes;
@property (nonatomic, readonly, copy, nullable) NSDictionary<NSString *, id> *childNodes;
@property (nonatomic, readonly, copy, nullable) NSString *nodeName;
@property (nonatomic, readonly, copy, nullable) NSArray<NSString *> *comments;
@property (nonatomic, readonly, copy, nullable) NSString *innerText;

@end

@implementation NSDictionary(XMLDictionary)

- (nullable NSString *)nodeName {
    return self[XMLDictionaryNodeNameKey];
}

- (nullable NSDictionary<NSString *, NSString *> *)attributes {
    NSDictionary<NSString *, NSString *> *attributes = self[XMLDictionaryAttributesKey];
    if (attributes) {
        return attributes.count? attributes: nil;
    } else {
        NSMutableDictionary<NSString *, id> *filteredDict = [NSMutableDictionary dictionaryWithDictionary:self];
        [filteredDict removeObjectsForKeys:@[XMLDictionaryCommentsKey, XMLDictionaryTextKey, XMLDictionaryNodeNameKey]];
        for (NSString *key in filteredDict.allKeys) {
            [filteredDict removeObjectForKey:key];
            if ([key hasPrefix:XMLDictionaryAttributePrefix]) {
                filteredDict[[key substringFromIndex:XMLDictionaryAttributePrefix.length]] = self[key];
            }
        }
        return filteredDict.count? filteredDict: nil;
    }
    return nil;
}

- (nullable NSDictionary *)childNodes {
    NSMutableDictionary *filteredDict = [self mutableCopy];
    [filteredDict removeObjectsForKeys:@[XMLDictionaryAttributesKey, XMLDictionaryCommentsKey, XMLDictionaryTextKey, XMLDictionaryNodeNameKey]];
    for (NSString *key in filteredDict.allKeys) {
        if ([key hasPrefix:XMLDictionaryAttributePrefix]) {
            [filteredDict removeObjectForKey:key];
        }
    }
    return filteredDict.count? filteredDict: nil;
}

- (nullable NSArray *)comments {
    return self[XMLDictionaryCommentsKey];
}

- (id)innerText {
    id text = self[XMLDictionaryTextKey];
    if ([text isKindOfClass:[NSArray class]]) {
        return [text componentsJoinedByString:@"\n"];
    } else {
        return text;
    }
}

@end

@interface XMLDictionary ()
@property (nonatomic, strong) NSMutableDictionary<NSString *, id> *root;
@property (nonatomic, strong) NSMutableArray *stack;
@property (nonatomic, strong) NSMutableString *text;
@property (nonatomic, assign) BOOL collapseTextNodes; // defaults to YES 只包含文本，没有子项，属性或注释的标签将被折叠成单个字符串对象，从而简化对象树的遍历。
@property (nonatomic, assign) BOOL stripEmptyNodes;   // defaults to YES 空的标签（没有子项，属性，文本或注释）将被剥离
@property (nonatomic, assign) BOOL trimWhiteSpace;    // defaults to YES 本节点修剪前导和尾随的空格，并且仅从字典中省略只包含空格的文本节点。
@property (nonatomic, assign) BOOL alwaysUseArrays;   // defaults to NO 如果是，则每个子节点将被表示为数组，即使只有其中一个。 这简化了处理可能被重复的属性所需的逻辑，因为您不需要使用[value isKindOfClass：[NSArray class]]来检查单数情况。 默认为NO。
@property (nonatomic, assign) BOOL preserveComments;  // defaults to NO
@property (nonatomic, assign) BOOL wrapRootNode;      // defaults to NO
@property (nonatomic, assign) XMLDictionaryAttributesMode attributesMode;
@property (nonatomic, assign) XMLDictionaryNodeNameMode nodeNameMode;

@end
@implementation XMLDictionary

#pragma mark - 初始化
+ (NSDictionary *)dictionaryWithXML:(NSXMLParser *)parser {
    XMLDictionary *dic = [[XMLDictionary alloc] init];
    return [dic dictionaryWithParser:parser];
}

- (instancetype)init {
    if ((self = [super init]))
    {
        _collapseTextNodes = YES;
        _stripEmptyNodes = YES;
        _trimWhiteSpace = YES;
        _alwaysUseArrays = NO;
        _preserveComments = NO;
        _wrapRootNode = NO;
    }
    return self;
}

- (NSDictionary<NSString *, id> *)dictionaryWithParser:(NSXMLParser *)parser {
    parser.delegate = (id<NSXMLParserDelegate>)self;
    [parser parse];
    id result = _root;
    _root = nil;
    _stack = nil;
    _text = nil;
    return result;
}

#pragma mark - NSXMLParserDelegate
- (void)parser:(__unused NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(__unused NSString *)namespaceURI qualifiedName:(__unused NSString *)qName attributes:(NSDictionary *)attributeDict{
    [self endText];
    [self stackAddNodWithElement:elementName andNode:[self nodeWithElement:elementName attributes:attributeDict]];
}

- (void)parser:(__unused NSXMLParser *)parser didEndElement:(__unused NSString *)elementName namespaceURI:(__unused NSString *)namespaceURI qualifiedName:(__unused NSString *)qName {
    [self endText];
    [self stackEnd];
}

- (void)parser:(__unused NSXMLParser *)parser foundCharacters:(NSString *)string {
    [self addText:string];
}

- (void)parser:(__unused NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock {
    NSString *CDATAText = [[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding];
    [self addText:CDATAText];
}

- (void)parser:(__unused NSXMLParser *)parser foundComment:(NSString *)comment {
    [self addComment:comment];
}

#pragma mark - privite
- (void)addComment:(NSString *)comment {
    if (_preserveComments) {
        NSMutableDictionary<NSString *,id> *top = _stack.lastObject;
        NSMutableArray<NSString *>*comments = top[XMLDictionaryCommentsKey];
        if (!comments) {
            comments = [@[comments] mutableCopy];
            top[XMLDictionaryCommentsKey] = comments;
        } else {
            [comments addObject:comment];
        }
    }
}

- (void)addText:(NSString *)text {
    if (!_text) {
        _text = [NSMutableString stringWithString:text];
    } else {
        [_text appendString:text];
    }
    
}

- (void)endText {
    if (_trimWhiteSpace) {
        _text = [[_text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy];
        
    }
    if (_text.length) {
        NSMutableDictionary *top = _stack.lastObject;
        id existing = top[XMLDictionaryTextKey];
        if ([existing isKindOfClass:[NSArray class]]) {
            [existing addObject:_text];
        } else if (existing) {
            top[XMLDictionaryTextKey] = [@[existing,_text] mutableCopy];
        } else {
            top[XMLDictionaryTextKey] = _text;
        }
    }
    _text = nil;
}

- (void)stackEnd {
    NSMutableDictionary <NSString *, id>*top = _stack.lastObject;
    [_stack removeLastObject];
    if (!top.attributes && !top.childNodes && !top.comments) {
        NSMutableDictionary<NSString *, id> *newTop = _stack.lastObject;
        NSString *nodeName = [self nameForNode:top inDictionary:newTop];
        if (nodeName) {
            id parentNode = newTop[nodeName];
            NSString *innerText = top.innerText;
            if (innerText && _collapseTextNodes) {
                if ([parentNode isKindOfClass:[NSArray class]]) {
                    parentNode[[parentNode count] - 1] = innerText;
                } else {
                    newTop[nodeName] = innerText;
                }
            } else if (!innerText) {
                if (_stripEmptyNodes) {
                    if ([parentNode isKindOfClass:[NSArray class]]) {
                        [(NSMutableArray *)parentNode removeLastObject];
                    } else {
                        [newTop removeObjectForKey:nodeName];
                    }
                } else if (!_collapseTextNodes) {
                    top[XMLDictionaryTextKey] = @"";
                }
            }
        }
    }
}

- (NSMutableDictionary<NSString *,id> *)nodeWithElement:(NSString *)elementName attributes:(NSDictionary *)attributeDict{
    NSMutableDictionary<NSString *,id> *node = [NSMutableDictionary dictionary];
    switch (_nodeNameMode) {
        case XMLDictionaryNodeNameModeRootOnly:{
            if (!_root) {
                node[XMLDictionaryNodeNameKey] = elementName;
            }
        }
            break;
        case XMLDictionaryNodeNameModeAlways:{
            node[XMLDictionaryNodeNameKey] = elementName;
        }
            break;
        case XMLDictionaryNodeNameModeNever:
            break;
    }
    if (attributeDict.count) {
        switch (_attributesMode) {
            case XMLDictionaryAttributesModePrefixed:{
                [attributeDict enumerateKeysAndObjectsUsingBlock:^(NSString *key, id  _Nonnull obj, BOOL * _Nonnull stop) {
                    NSString * attributekey = [XMLDictionaryAttributePrefix stringByAppendingString:key];
                    node[attributekey] = obj;
                }];
            }
                break;
            case XMLDictionaryAttributesModeDictionary:{
                node[XMLDictionaryAttributesKey] = attributeDict;
            }
                break;
            case XMLDictionaryAttributesModeUnprefixed:{
                [node addEntriesFromDictionary:attributeDict];
            }
                break;
            case XMLDictionaryAttributesModeDiscard:
                break;
        }
    }
    return node;
}

- (void)stackAddNodWithElement:(NSString *)elementName andNode:(NSMutableDictionary<NSString *,id> *)node {
    if (!_root) {
        _root = node;
        _stack = [NSMutableArray arrayWithObject:node];
        if (_wrapRootNode) {
            _root = [NSMutableDictionary dictionaryWithObject:_root forKey:elementName];
            [_stack insertObject:_root atIndex:0];
        }
    } else {
        //取出堆中最后一项
        NSMutableDictionary<NSString *, id> *top = _stack.lastObject;
        //取出对应的数值
        id existing = top[elementName];
        //如果是数组  数组增加node
        if ([existing isKindOfClass:[NSArray class]]) {
            [(NSMutableArray *)existing addObject:node];
        } else if (existing) {
            top[elementName] = [@[existing, node] mutableCopy];
        } else if (_alwaysUseArrays) {
            top[elementName] = [NSMutableArray arrayWithObject:node];
        } else {
            top[elementName] = node;
        }
        [_stack addObject:node];
    }
}

- (NSString *)nameForNode:(NSDictionary<NSString *, id> *)node inDictionary:(NSDictionary<NSString *, id> *)dict {
    if (node.nodeName) {
        return node.nodeName;
    } else {
        for (NSString *name in dict) {
            id object = dict[name];
            if (object == node) {
                return name;
            } else if ([object isKindOfClass:[NSArray class]] && [(NSArray *)object containsObject:node]) {
                return name;
            }
        }
    }
    return nil;
}

@end


